import { ConfigPreview } from "@/docs/components/Preview";

# Dynamic Fields

Dynamic field resolution allows you to change the [field configuration](/docs/api-reference/configuration/component-config#fields) for a component based on the current component props.

## Dynamic component fields

The [`resolveFields` function](/docs/api-reference/configuration/component-config#resolvefieldsdata-params) allows you to make synchronous and asynchronous changes to the field configuration.

For example, we can set the configuration of one field based on the prop value of another:

```tsx {4-25} showLineNumbers copy
const config = {
  components: {
    MyComponent: {
      resolveFields: (data) => {
        const fields = {
          drink: {
            type: "radio",
            options: [
              { label: "Water", value: "water" },
              { label: "Orange juice", value: "orange-juice" },
            ],
          },
        };

        if (data.props.drink === "water") {
          return {
            ...fields,
            waterType: {
              // ... Define field
            },
          };
        }

        return fields;
      },
      // ...
    },
  },
};
```

<ConfigPreview
  label='Try changing the "drink" field'
  componentConfig={{
    resolveFields: (data) => {
      const fields = {
        drink: {
          type: "radio",
          options: [
            { label: "Water", value: "water" },
            { label: "Orange juice", value: "orange-juice" },
          ],
        },
      };

      if (data.props.drink === "water") {
        return {
          ...fields,
          waterType: {
            type: "radio",
            options: [
              { label: "Still", value: "still" },
              { label: "Sparkling", value: "sparkling" },
            ],
          },
        };
      }

      return fields;
    },
    defaultProps: {
      drink: "water",
      waterType: "still",
    },
    render: ({ drink, waterType }) => (
      <p>
        {drink}
        {drink === "water" ? ` (${waterType})` : ""}
      </p>
    ),

}}
/>

### Making asynchronous calls

The [`resolveFields` function](/docs/api-reference/configuration/component-config#resolvefieldsdata-params) also enables asynchronous calls.

Here's an example populating the options for a [`select` field](/docs/api-reference/fields/select) based on a [`radio` field](/docs/api-reference/fields/radio)

```tsx {4-24} showLineNumbers copy
const config = {
  components: {
    MyComponent: {
      resolveFields: async (data, { changed, lastFields }) => {
        // Don't call the API unless `category` has changed
        if (!changed.category) return lastFields;

        // Make an asynchronous API call to get the options
        const options = await getOptions(data.category);

        return {
          category: {
            type: "radio",
            options: [
              { label: "Fruit", value: "fruit" },
              { label: "Vegetables", value: "vegetables" },
            ],
          },
          item: {
            type: "select",
            options,
          },
        };
      },
      render: ({ item }) => <h1>{item}</h1>,
    },
  },
};
```

<ConfigPreview
  label='Try changing the "category" field'
  componentConfig={{
    resolveFields: async (data, { changed, lastFields }) => {
      if (!changed.category) return lastFields;

      await new Promise((resolve) => setTimeout(resolve, 500));

      return {
        category: {
          type: "radio",
          options: [
            { label: "Fruit", value: "fruit" },
            { label: "Vegetables", value: "vegetables" },
          ],
        },
        item: {
          type: "select",
          options:
            data.props.category === "fruit"
              ? [
                { label: "Select a fruit", value: "" },
                { label: "Apple", value: "apple" },
                { label: "Orange", value: "orange" },
                { label: "Tomato", value: "tomato" }
              ] : [
                { label: "Select a vegetable", value: "" },
                { label: "Broccoli", value: "broccoli" },
                { label: "Cauliflower", value: "cauliflower" },
                { label: "Mushroom", value: "mushroom" },
              ],
        },
      };
    },

    defaultProps: {
      category: "fruit",
      item: "",
    },
    render: ({ item }) => <p>{item}</p>,

}}
/>

## Limitations

The [`slot` field](/docs/api-reference/fields/slot) is not currently supported by Dynamic Fields, but most use-cases can be achieved using [Dynamic Props](dynamic-props).

## Further reading

- [`resolveFields` API reference](/docs/api-reference/configuration/component-config#resolvefieldsdata-params)
